import bluetooth
import threading
import time

from util.decorators import *

# TODO: Work out what this has to be
MAX_BUF_SIZE = 1024

import logging
logger = logging.getLogger('async.bt')

class _L2CAPReceiveChannel(threading.Thread):
    def __init__(self, conn, addr, receive_cb, close_cb):
        threading.Thread.__init__(self)
        self.__conn = conn
        self.__addr = addr
        self.__receive_cb = receive_cb
        self.__close_cb = close_cb
        self.__closed = False
        self.start()
    
    def run(self):
        while not self.__closed:
            data = self.__conn.recv(MAX_BUF_SIZE)
            print 'L2CAP Received',len(data),'bytes'
            if data in ['x00', '']:
                break
            self.__receive_cb(data, self.__addr)
        self.__close_cb(self.__addr)

    def close(self):
        print 'L2CAP: Closing receiver'
        self.__closed = True

class _L2CAPSendChannel(threading.Thread):
    def __init__(self, addr):
        threading.Thread.__init__(self)
        self.__sock = bluetooth.BluetoothSocket(bluetooth.L2CAP)
        print 'L2CAP: Connecting to',addr
        self.__sock.connect(addr)
        print 'L2CAP: Connected.'
        self.__closed = False
        self.__queue = []
        self.start()

    def run(self):
        while not self.__closed:
            print 'L2CAP: In loop.'
            if len(self.__queue) > 0:
                data = self.__queue.pop(0)
                data = data + '\n'
                print 'L2CAP: Sending',len(data),'bytes'
                logger.debug('Sending %s bytes: %s', len(data), data)
                self.__sock.send(data)
                if data == '\x00':
                    self.close()
            else:
                pass
            time.sleep(0.005)
        print 'L2CAP: Send channel closed.'
        self.__sock.close()
        
    def send(self, data):
        print 'L2CAP: Appending data to queue'
        self.__queue.append(data)

    def close(self):
        print 'L2CAP: Set __closed = True'
        self.__closed = True
            
class _L2CAPListener(threading.Thread):
    
    def __init__(self, port, received):
        threading.Thread.__init__(self)
        self.__listener = bluetooth.BluetoothSocket(bluetooth.L2CAP)
        self.__listener.bind(('', port))
        self.__listener.setblocking(True)
        self.__listener.listen(1)
        self.__closed = False
        self.__received = received
        self.__receivers = {}
        self.__lock = threading.Lock()
        self.start()
    
    def run(self):
        while not self.__closed:
            print '~*~*~ Listener: Trying accept'
            conn, addr = self.__listener.accept()
            self.__lock.acquire()
            (mac, port) = addr
            if mac in self.__receivers:
                assert False
            else:
                print 'L2CAP: Accepted connection from',conn,time.time()
                self.__receivers[mac] = _L2CAPReceiveChannel(conn, addr, self.__received, self.__on_finished)
            self.__lock.release()

    def close(self):
        self.__closed = True

    def __on_finished(self, addr):
        self.__lock.acquire()
        print 'L2CAP: Connection closed',addr
        (mac, port) = addr
        print 'L2CAP: receivers :',self.__receivers
        self.__receivers[mac].close()
        del self.__receivers[mac]
        print 'L2CAP: Active receivers:',self.__receivers
        self.__lock.release()
        
class Unicast(threading.Thread):

    def __init__(self, port, queue, recv_func):
        threading.Thread.__init__(self)
        self.__port = port
        self.__queue = queue
        self.__listener = _L2CAPListener(self.__port, recv_func)
        self.__closed = False      
        self.__senders = {}
        self.start()
        
    def run(self):
        while not self.__closed:
            if len(self.__queue) > 0:
                (data, (mac, port)) = self.__queue.next()
                print 'L2CAP: Got item from queue',len(data),mac,port,time.time()
                if data == None:
                    print 'L2CAP: Closing send channel'
                    self.__senders[mac].send('\x00')
                    self.__senders[mac].close()
                    del self.__senders[mac]
                else:
                    if mac in self.__senders:
                        print 'L2CAP: Sending data to existing channel'
                        self.__senders[mac].send(data)
                    else:
                        print 'L2CAP: Creating channel and sending data'
                        self.__senders[mac] = _L2CAPSendChannel((mac, port))
                        self.__senders[mac].send(data)
            time.sleep(0.005)
        print 'L2CAP: Done'
        
        def close(self):
            self.__closed = True

class Broadcast(threading.Thread):

    def __init__(self, port, queue, recv_func):
        pass